﻿using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
#if NETFX_CORE
using Windows.UI.Core;
using Windows.UI.Xaml;
#endif

namespace UnitDriven
{
	public sealed class UnitTestContext : IDisposable
	{
		private DateTime timerStarted = DateTime.MaxValue;
		private TimeSpan timeout = TimeSpan.MaxValue;
        private object lockObject = new object();
#if NETFX_CORE
        private CoreDispatcher _dispatcher = Window.Current.Dispatcher;
#endif

		public event EventHandler<AsyncTestCompleteEventArgs> Completed;

        public UnitTestContext()
        {
            _asserter = new AsyncAsserter();
            _asserter.Complete += OnComplete;
        }

		private void OnComplete(object sender, AsyncAssertCompleteEventArgs e)
		{
			if (_asserter != null)
				_asserter.Complete -= OnComplete;

			if (Completed != null)
				Completed(this, new AsyncTestCompleteEventArgs(e.Result, e.Error));
		}

		private AsyncAsserter _asserter;

		public AsyncAsserter Assert
		{
            get { return _asserter; }
		}

#if NETFX_CORE
		public async void SetTimeout(TimeSpan timeout)
		{
			this.timerStarted = DateTime.Now;
			this.timeout = timeout;
            await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, TimerCallback);
        }
#elif __ANDROID__
        public void SetTimeout(TimeSpan timeout)
		{
			this.timerStarted = DateTime.Now;
			this.timeout = timeout;
			System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(TimerCallback);
        }
#else
		public void SetTimeout(TimeSpan timeout)
		{
			this.timerStarted = DateTime.Now;
			this.timeout = timeout;
			Application.Current.RootVisual.Dispatcher.BeginInvoke(TimerCallback);
        }
#endif


#if __ANDROID__
		private void TimerCallback()
		{
			if (DateTime.Now - this.timeout >= this.timerStarted)
				Assert.Fail("Test timed out.");
			System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(TimerCallback);
        }
#elif NETFX_CORE
		private async void TimerCallback()
		{
			if (DateTime.Now - this.timeout >= this.timerStarted)
				Assert.Fail("Test timed out.");
            await Window.Current.Content.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, TimerCallback);
        }
#else
		private void TimerCallback()
		{
			if (DateTime.Now - this.timeout >= this.timerStarted)
				Assert.Fail("Test timed out.");
			Application.Current.RootVisual.Dispatcher.BeginInvoke(TimerCallback);
        }
#endif

		public void Complete()
		{
		}

		public void Dispose()
		{
		}
	}

	public sealed class AsyncTestCompleteEventArgs : EventArgs
	{
		private Exception _error;
		private TestResult _status;

		public Exception Error { get { return _error; } }
		public TestResult Status { get { return _status; } }

		public AsyncTestCompleteEventArgs(TestResult status, Exception error)
		{
			_status = status;
			_error = error;
		}
	}
}
